iT邦幫忙

0

Java中反序列化帶有Iterable類型field的對象?

  • 分享至 

  • xImage
  •  

前情提要

Device sampleDevice = JSON.parseObject(str1, Device.class);

在使用 fastjson 將字符串直接反序列化 Java 對象的時候(而不是通過 JSON 一個 feild 一個 feild 的解析來構造 pojo,遇到覆雜對象會很難讓人看懂代碼寫的是啥),遇到了報錯:

Exception in thread "main" com.alibaba.fastjson.JSONException: syntax error, expect {, actual [, pos 105, fieldName fun, fastjson-version 1.2.47
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:451)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:271)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:267)
	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:661)
	at com.alibaba.fastjson.parser.deserializer.MapDeserializer.parseMap(MapDeserializer.java:192)
	at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:59)
	at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:41)
	at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_Device.deserialze(Unknown Source)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:267)
	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:661)
	at com.alibaba.fastjson.JSON.parseObject(JSON.java:365)
	at com.alibaba.fastjson.JSON.parseObject(JSON.java:269)
	at com.alibaba.fastjson.JSON.parseObject(JSON.java:488)
	at serialization_tutorial.main.main(main.java:53)

Process finished with exit code 1

意思大概是 fieldName fun 里面的數據無法正常解析,這是因為 fun 所對應的數據形態是一個 Iterable,導致 fastjson 無法解析。範例代碼如下:

@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Device {
	@Getter
	@Setter
	private String deviceNo;

	@Getter
	@Setter
	private GradeWithValue grade;

	@Getter
	@Setter
	private Status Status;

	@Getter
	@Setter
	private String description;

	@Getter
	@Setter
	private Timezone timeZone;

	@Getter
	@Setter
	//	@JSONField(serializeUsing=SupportAppNamesCodec.class, deserializeUsing=SupportAppNamesCodec.class)
	private HashMap<String, Iterator<AppName>> supportAppNames;

	public Device (String deviceNo, GradeWithValue grade) {
		this.deviceNo = deviceNo;
		this.grade = grade;
	}
}

解決方法

  1. 將反序列化邏輯封裝在自定義的 deserializer 和 serializer,並通過注解指定 fastjson 在序列化/反序列化的時候使用的工具類。這種方法代碼需根據實際業務修改,只是將原本的 JSON 字符串反序列化操作進行封裝,代碼無法覆用。

    @JSONField(serializeUsing=SupportAppNamesCodec.class, deserializeUsing=SupportAppNamesCodec.class)
    
  2. 在類中添加某個 List 屬性時,為了不讓調用者直接通過引用修改數據,所以將 get 方法設計成返回一個不可變集合

    • 返回 Iterable (記得要 disable the remove api, 其實就是 Collections.unmodifiableList) ,或者 Enumeration

      在類中添加某個 List 屬性時,為了不讓調用者進行修改,所以在該屬性的 get 方法時實際返回的是一個 Iterable,即 List 所實現的一個接口,get 方法封裝了類對於該屬性的實現。因為 Iterable 接口中方法只包含用於遍歷的相關方法,不包含改變內部元素的方法。如果 get 方法返回的是 List 類型,那麽就暴露了類使用 List 來儲存該屬性,並且調用者可以簡單地通過得到的 List 對象來對類實例的內部數據進行修改,容易发生危險的事情。

    • 返回 java.util.Collections.unmodifiableList,在下面的 stackexchange 里有說明。當一下搬運工。

      public class Event {
      
        private List<Player> players;
      
        public List<Player> getPlayers() {
          return Collections.unmodifiableList(players);
        }
      
        public void addPlayer(Player p) {
          players.add(p);
        }
      }
      
    • 返回 Guava's com.google.common.collect.ImmutableList (進一步保證了 thread-safe)

      	public ImmutableList<Timezone> getSupportedTimezone() {
      		return ImmutableList.copyOf(supportedTimezone);
      	}
      

      另外,fastJson 不支持直接反序列化 ImmutableList。[3]

      Exception in thread "main" com.alibaba.fastjson.JSONException: create instance error, class com.google.common.collect.ImmutableList
      	at com.alibaba.fastjson.util.TypeUtils.createCollection(TypeUtils.java:2082)
      	at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_2_AppName.deserialze(Unknown Source)
      	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:267)
      	at com.alibaba.fastjson.parser.DefaultJSONParser.parseArray(DefaultJSONParser.java:747)
      	at com.alibaba.fastjson.serializer.CollectionCodec.deserialze(CollectionCodec.java:129)
      	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:661)
      	at com.alibaba.fastjson.parser.deserializer.MapDeserializer.parseMap(MapDeserializer.java:192)
      	at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:59)
      	at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:41)
      	at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.java:86)
      	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField(JavaBeanDeserializer.java:1078)
      	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:773)
      	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest(JavaBeanDeserializer.java:1283)
      	at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_Device.deserialze(Unknown Source)
      	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:267)
      	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:661)
      	at com.alibaba.fastjson.JSON.parseObject(JSON.java:365)
      	at com.alibaba.fastjson.JSON.parseObject(JSON.java:269)
      	at com.alibaba.fastjson.JSON.parseObject(JSON.java:488)
      	at serialization_tutorial.main.main(main.java:54)
      
      Process finished with exit code 1  
      

結論

不要對 Iterable、ImmutableList 進行反序列化,非常反人類。直接在 get 方法返回你想要的數據形態就可以啦~ fastJson 解析 json 字符串的時候是首先根據 set 方法的數據形態,然後根據 field 的數據形態來決定的,而 get 方法貌似只要能正常得到數據都是可以的。


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言